package thread

import (
	"errors"
	"fmt"
	"gitee.com/sansaniot/ssiot-core/facade/env"
	"gitee.com/sansaniot/ssiot-core/logger"
	"ssdevice/common/constant"
	cDto "ssdevice/common/dto"
	"ssdevice/common/enums"
	"ssdevice/common/middleware/mqtt"
	"ssdevice/common/utils"
	"ssdevice/internal/device/models"
	"ssdevice/internal/device/models/query"
	"time"
)

type LogoutTimeoutThread struct {
}

type Device struct {
	sn            string
	logoutTimeout int
	lastLiveTime  time.Time
}

func (l LogoutTimeoutThread) Run(threadName string) {
	logger.Infof("启动线程 - %s.", threadName)

	go func() {
		logoutDeviceBatch()
	}()
}

func logout(sn string) {
	topic := fmt.Sprintf("/iot/$/%s/logout", sn)
	payload := utils.MapToBytes(map[string]interface{}{})
	mqtt.MqttClient.Publish(topic, payload)
}

func logoutDevices(deviceList []*Device) {
	for _, dev := range deviceList {
		if dev.logoutTimeout <= 0 {
			continue
		}

		if time.Now().Sub(dev.lastLiveTime).Seconds() > float64(dev.logoutTimeout) {
			logout(dev.sn)
		}
	}
}

func logoutDeviceBatch() {
	index := 1
	// 每1000条处理一次
	for {
		time.Sleep(3 * time.Second)
		_, deviceList := getLogoutDevices(index, 1000)
		if len(deviceList) == 0 {
			index = 1
			continue
		}

		logoutDevices(deviceList)
		index++
	}
}

// 根据产品查询出配置了超时的设备，设备表 productModel 要加索引
func getLogoutDevices(index int, size int) (err error, logoutDeviceList []*Device) {
	logoutProductsModel := make([]string, 0)

	// 所有配置了超时时间，并且超时时间 >0 的产品
	constant.ProductConfigCache.Range(func(key, value any) bool {
		if value.(*constant.ProductConfig).EnableLogoutTimeout() {
			logoutProductsModel = append(logoutProductsModel, key.(string))
			return true
		}

		return true
	})

	if len(logoutProductsModel) == 0 {
		return errors.New("no logout device"), nil
	}

	deviceModel := models.SysDev{}
	deviceModel.Orm = env.Db
	// 查询设备的条件
	cond := &query.SysDevQuery{}
	cond.PageIndex = index
	cond.PageSize = size
	cond.OnlineStatus = enums.Online
	cond.ProductModelIn = logoutProductsModel

	err, deviceListAny := deviceModel.FindDeviceList(cond)
	if err != nil {
		return err, nil
	}

	devicePage := deviceListAny.(*cDto.Page)
	deviceList := devicePage.Records.([]models.SysDev)

	for _, dev := range deviceList {
		productConfig, ok := constant.ProductConfigCache.Load(dev.ProductModel)
		if !ok {
			continue
		}

		if dev.LastLiveTime == nil {
			continue
		}

		logoutDevice := &Device{}
		logoutDevice.sn = dev.Sn
		logoutDevice.logoutTimeout = productConfig.(*constant.ProductConfig).LogoutTimeout
		logoutDevice.lastLiveTime = time.Time(*dev.LastLiveTime)
		logoutDeviceList = append(logoutDeviceList, logoutDevice)
	}

	return nil, logoutDeviceList
}
